/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { IGlobalConfig, IScheduleResource } from '../interface';
import { getDaysWithWeekdayPerMonthMap, flattenMapValues } from '../utils';
import { GlobalConfigController } from './global-config.controller';
import { UICoordinateController } from './ui-coordinate.controller';

/**
 * @description 背景层控制器
 * @export
 * @class BgLayerController
 */
export class BgLayerController {
  /**
   * @description 全局配置控制器
   * @private
   * @type {GlobalConfigController}
   * @memberof BgLayerController
   */
  private configController!: GlobalConfigController;

  /**
   * @description 事件控制器
   * @private
   * @type {any}
   * @memberof BgLayerController
   */
  private evt: any;

  /**
   * @description 头部canvas元素
   * @private
   * @type {(HTMLCanvasElement | null)}
   * @memberof BgLayerController
   */
  private headerCanvas: HTMLCanvasElement | null = null;

  /**
   * @description 头部canvas上下文
   * @private
   * @type {(CanvasRenderingContext2D | null)}
   * @memberof BgLayerController
   */
  private headerCtx: CanvasRenderingContext2D | null = null;

  /**
   * @description 内容体canvas元素
   * @private
   * @type {(HTMLCanvasElement | null)}
   * @memberof BgLayerController
   */
  private bodyCanvas: HTMLCanvasElement | null = null;

  /**
   * @description 内容体canvas上下文
   * @private
   * @type {(CanvasRenderingContext2D | null)}
   * @memberof BgLayerController
   */
  private bodyCtx: CanvasRenderingContext2D | null = null;

  /**
   * @description 坐标控制器
   * @private
   * @type {(UICoordinateController | null)}
   * @memberof BgLayerController
   */
  private uiCoordinate: UICoordinateController | null = null;

  /**
   * @description 激活的单元格位置
   * @private
   * @type {{ row: number; col: number } | null}
   * @memberof BgLayerController
   */
  private activeCell: { row: number; col: number } | null = null;

  /**
   * @description 备份资源（用于背景重绘逻辑使用）
   * @private
   * @type {IScheduleResource[]}
   * @memberof BgLayerController
   */
  private copyResources: IScheduleResource[] = [];

  /**
   * Creates an instance of BgLayerController.
   * @param {GlobalConfigController} configController
   * @memberof BgLayerController
   */
  constructor(configController: GlobalConfigController, evt: any) {
    this.configController = configController;
    this.evt = evt;
    this.handleCanvasClick = this.handleCanvasClick.bind(this);
    this.handleDocumentClick = this.handleDocumentClick.bind(this);
  }

  /**
   * @description 初始化
   * @param {HTMLCanvasElement} headerCanvas
   * @param {HTMLCanvasElement} bodyCanvas
   * @memberof BgLayerController
   */
  init(headerCanvas: HTMLCanvasElement, bodyCanvas: HTMLCanvasElement): void {
    this.headerCanvas = headerCanvas;
    this.headerCtx = this.headerCanvas.getContext('2d');
    this.bodyCanvas = bodyCanvas;
    this.bodyCtx = this.bodyCanvas.getContext('2d');
    // 添加点击事件监听
    this.bodyCanvas.addEventListener('click', this.handleCanvasClick);
    // 添加文档点击事件监听，用于清除激活状态
    document.addEventListener('click', this.handleDocumentClick);
  }

  /**
   * @description 处理canvas点击事件
   * @private
   * @param {MouseEvent} event
   * @returns {*}  {void}
   * @memberof BgLayerController
   */
  private handleCanvasClick(event: MouseEvent): void {
    if (!this.bodyCanvas || !this.uiCoordinate) return;

    const config = this.configController.getConfig();
    const rect = this.bodyCanvas.getBoundingClientRect();
    const x = event.clientX - rect.left;
    const y = event.clientY - rect.top;

    // 计算点击的单元格行列
    const col = Math.floor(
      (x - config.resourceColumnWidth - config.scaleColumnWidth) /
        this.uiCoordinate.cellWidth,
    );
    const row = Math.floor(y / this.uiCoordinate.cellHeight);

    // 检查点击是否在有效区域内
    if (col >= 0 && row >= 0) {
      this.activeCell = { row, col };
    } else {
      this.activeCell = null;
    }
    this.redraw();
    // 阻止事件冒泡，避免触发document的点击事件
    event.stopPropagation();
  }

  /**
   * @description 处理文档点击事件
   * @private
   * @param {MouseEvent} event
   * @returns {*}  {void}
   * @memberof BgLayerController
   */
  private handleDocumentClick(event: MouseEvent): void {
    // 如果点击的不是bodyCanvas元素，则清除激活状态
    if (this.bodyCanvas && !this.bodyCanvas.contains(event.target as Node)) {
      this.activeCell = null;
      this.redraw();
    }
  }

  /**
   * @description 重新绘制canvas
   * @private
   * @memberof BgLayerController
   */
  private redraw(): void {
    if (!this.uiCoordinate) return;
    this.draw(this.uiCoordinate, this.copyResources);
  }

  /**
   * @description 绘制激活单元格
   * @private
   * @param {IGlobalConfig} config
   * @memberof BgLayerController
   */
  private drawActiveCell(config: IGlobalConfig): void {
    if (
      !this.bodyCanvas ||
      !this.bodyCtx ||
      !this.uiCoordinate ||
      !this.activeCell
    )
      return;

    const { row, col } = this.activeCell;
    const x =
      config.resourceColumnWidth +
      config.scaleColumnWidth +
      col * this.uiCoordinate.cellWidth;
    const y = row * this.uiCoordinate.cellHeight;
    const width = this.uiCoordinate.cellWidth;
    const height = this.uiCoordinate.cellHeight;

    // 绘制激活状态背景（淡黄色）
    this.bodyCtx.fillStyle = 'rgba(255, 255, 0, 0.3)';
    this.bodyCtx.fillRect(x, y, width, height);

    // 绘制激活状态边框
    this.bodyCtx.strokeStyle = 'rgba(255, 255, 0, 0.8)';
    this.bodyCtx.lineWidth = 2;
    this.bodyCtx.strokeRect(x, y, width, height);
  }

  /**
   * @description 清空canvas
   * @memberof BgLayerController
   */
  private clearCanvas(): void {
    if (!this.headerCanvas || !this.headerCtx) return;
    this.headerCtx.clearRect(
      0,
      0,
      this.headerCanvas.width,
      this.headerCanvas.height,
    );
    if (!this.bodyCanvas || !this.bodyCtx) return;
    this.bodyCtx.clearRect(0, 0, this.bodyCanvas.width, this.bodyCanvas.height);
  }

  /**
   * @description 初始化画布大小
   * @param {IGlobalConfig} config
   * @param {IScheduleResource[]} resources
   * @returns {*}  {void}
   * @memberof BgLayerController
   */
  private initCanvasSize(config: IGlobalConfig): void {
    if (!this.headerCanvas || !this.bodyCanvas || !this.uiCoordinate) return;
    this.headerCanvas.width = this.uiCoordinate.canvasWidth;
    this.headerCanvas.height = config.headerRowHeight * 2;
    this.bodyCanvas.width = this.uiCoordinate.canvasWidth;
    this.bodyCanvas.height = this.uiCoordinate.canvasHeight;
  }

  /**
   * @description 绘制头部
   * @param {IGlobalConfig} config
   * @memberof BgLayerController
   */
  private drawHeader(config: IGlobalConfig): void {
    if (!this.headerCanvas || !this.headerCtx || !this.uiCoordinate) return;

    this.headerCtx.fillStyle = config.headerBgColor;
    this.headerCtx.fillRect(
      0,
      0,
      config.resourceColumnWidth,
      config.headerRowHeight * 2,
    );
    this.headerCtx.fillStyle = config.headerTextColor;
    this.headerCtx.textAlign = 'center';
    this.headerCtx.textBaseline = 'middle';
    this.headerCtx.fillText(
      '资源名称',
      config.resourceColumnWidth / 2,
      config.headerRowHeight,
    );

    this.headerCtx.fillStyle = config.headerBgColor;
    this.headerCtx.fillRect(
      config.resourceColumnWidth,
      0,
      config.scaleColumnWidth,
      config.headerRowHeight * 2,
    );
    this.headerCtx.fillStyle = config.headerTextColor;
    this.headerCtx.textAlign = 'center';
    this.headerCtx.textBaseline = 'middle';
    this.headerCtx.fillText(
      '刻度',
      config.resourceColumnWidth + config.scaleColumnWidth / 2,
      config.headerRowHeight,
    );

    const headerMap = getDaysWithWeekdayPerMonthMap(
      config.startTime,
      config.endTime,
    );
    const headerColumns = Array.from(headerMap.keys());
    const totalColumns = flattenMapValues(headerMap);
    // 开始绘制第一行
    let firstOffset = config.resourceColumnWidth + config.scaleColumnWidth;
    for (let i = 0; i < headerColumns.length; i++) {
      const month = headerColumns[i];
      const days = headerMap.get(month) || [];
      // 当前项第一行宽度
      const firstCurrentWidth = this.uiCoordinate.cellWidth * days.length;
      this.headerCtx.fillStyle = config.headerBgColor;
      this.headerCtx.fillRect(
        firstOffset,
        0,
        firstCurrentWidth,
        config.headerRowHeight,
      );
      this.headerCtx.fillStyle = config.headerTextColor;
      this.headerCtx.textAlign = 'center';
      this.headerCtx.textBaseline = 'middle';
      this.headerCtx.fillText(
        month,
        firstOffset + firstCurrentWidth / 2,
        config.headerRowHeight / 2,
      );
      firstOffset += firstCurrentWidth;
    }
    // 开始绘制第二行
    let secondOffset = config.resourceColumnWidth + config.scaleColumnWidth;
    for (let i = 0; i < totalColumns.length; i++) {
      this.headerCtx.fillStyle = config.headerBgColor;
      this.headerCtx.fillRect(
        secondOffset,
        config.headerRowHeight,
        this.uiCoordinate.cellWidth,
        config.headerRowHeight,
      );
      this.headerCtx.fillStyle = config.headerTextColor;
      this.headerCtx.textAlign = 'center';
      this.headerCtx.textBaseline = 'middle';
      this.headerCtx.fillText(
        totalColumns[i],
        secondOffset + this.uiCoordinate.cellWidth / 2,
        config.headerRowHeight + config.headerRowHeight / 2,
      );
      secondOffset += this.uiCoordinate.cellWidth;
    }
  }

  /**
   * @description 绘制资源、精度列背景
   * @param {IGlobalConfig} config
   * @memberof BgLayerController
   */
  private drawResourceAndScaleBg(config: IGlobalConfig): void {
    if (!this.bodyCanvas || !this.bodyCtx) return;
    this.bodyCtx.fillStyle = config.resourceBodyBg;
    this.bodyCtx.fillRect(
      0,
      0,
      config.resourceColumnWidth,
      this.bodyCanvas.height,
    );

    this.bodyCtx.fillStyle = config.scaleBodyBg;
    this.bodyCtx.fillRect(
      config.resourceColumnWidth,
      0,
      config.scaleColumnWidth,
      this.bodyCanvas.height,
    );
  }

  /**
   * @description 绘制网格线
   * @param {IGlobalConfig} config
   * @returns {*}  {void}
   * @memberof BgLayerController
   */
  private drawGridLines(config: IGlobalConfig): void {
    if (!this.bodyCanvas || !this.bodyCtx || !this.uiCoordinate) return;
    this.bodyCtx.strokeStyle = config.gridLineColor;
    this.bodyCtx.lineWidth = 1;

    // 左侧资源线
    let x = config.resourceColumnWidth;
    this.bodyCtx.beginPath();
    this.bodyCtx.moveTo(x, 0);
    this.bodyCtx.lineTo(x, this.bodyCanvas.height);
    this.bodyCtx.stroke();

    // 左侧刻度线
    x += config.scaleColumnWidth;
    this.bodyCtx.beginPath();
    this.bodyCtx.moveTo(x, 0);
    this.bodyCtx.lineTo(x, this.bodyCanvas.height);
    this.bodyCtx.stroke();

    // 中间日期线
    let offsetX = config.resourceColumnWidth + config.scaleColumnWidth;
    for (let i = 0; i < this.uiCoordinate.totalDays; i++) {
      this.bodyCtx.beginPath();
      this.bodyCtx.moveTo(offsetX, 0);
      this.bodyCtx.lineTo(offsetX, this.bodyCanvas.height);
      this.bodyCtx.stroke();
      offsetX += this.uiCoordinate.cellWidth;
    }

    // 水平线 (包括右侧小时标签列)
    const totalRows = Math.floor(
      this.bodyCanvas.height / this.uiCoordinate.cellHeight,
    );
    let startY = 0;
    for (let i = 0; i < totalRows; i++) {
      startY += this.uiCoordinate.cellHeight;
      this.bodyCtx.beginPath();
      this.bodyCtx.moveTo(
        config.resourceColumnWidth + config.scaleColumnWidth,
        startY,
      );
      this.bodyCtx.lineTo(this.bodyCanvas.width, startY);
      this.bodyCtx.stroke();
    }
  }

  /**
   * @description 绘制刻度分割线,刻度文本
   * @param {IGlobalConfig} config
   * @memberof BgLayerController
   */
  private drawScaleDrivers(
    config: IGlobalConfig,
    resources: IScheduleResource[],
  ): void {
    if (!this.bodyCanvas || !this.bodyCtx || !this.uiCoordinate) return;
    this.bodyCtx.strokeStyle = config.gridLineColor;
    this.bodyCtx.lineWidth = 1;
    let startY = 0;
    const singleResourceRows =
      (config.scaleRange[1] - config.scaleRange[0]) / config.scaleValue;
    if (resources.length === 0) {
      let dynaText = config.scaleRange[0];
      for (let i = 0; i < singleResourceRows; i++) {
        // 绘制刻度线
        startY += this.uiCoordinate.cellHeight;
        dynaText += config.scaleValue;
        this.bodyCtx.beginPath();
        this.bodyCtx.moveTo(
          config.resourceColumnWidth + (config.scaleColumnWidth / 3) * 2,
          startY,
        );
        this.bodyCtx.lineTo(
          config.resourceColumnWidth + config.scaleColumnWidth,
          startY,
        );
        this.bodyCtx.stroke();
        this.bodyCtx.fillStyle = config.scaleTextColor;
        this.bodyCtx.textAlign = 'center';
        this.bodyCtx.textBaseline = 'middle';
        if (i !== singleResourceRows - 1) {
          this.bodyCtx.fillText(
            dynaText.toString(),
            config.resourceColumnWidth + (config.scaleColumnWidth / 3) * 1,
            startY,
          );
        }
      }
      return;
    }
    for (let i = 0; i < resources.length; i++) {
      let dynaText = config.scaleRange[0];
      for (let j = 0; j < singleResourceRows; j++) {
        // 绘制刻度线
        startY += this.uiCoordinate.cellHeight;
        dynaText += config.scaleValue;
        this.bodyCtx.beginPath();
        this.bodyCtx.moveTo(
          config.resourceColumnWidth + (config.scaleColumnWidth / 3) * 2,
          startY,
        );
        this.bodyCtx.lineTo(
          config.resourceColumnWidth + config.scaleColumnWidth,
          startY,
        );
        this.bodyCtx.stroke();
        this.bodyCtx.fillStyle = config.scaleTextColor;
        this.bodyCtx.textAlign = 'center';
        this.bodyCtx.textBaseline = 'middle';
        if (j !== singleResourceRows - 1) {
          this.bodyCtx.fillText(
            dynaText.toString(),
            config.resourceColumnWidth + (config.scaleColumnWidth / 3) * 1,
            startY,
          );
        }
      }
    }
  }

  /**
   * @description 绘制资源分割线
   * @param {IGlobalConfig} config
   * @param {IScheduleResource[]} resources
   * @memberof BgLayerController
   */
  private drawResourceDrivers(
    config: IGlobalConfig,
    resources: IScheduleResource[],
  ): void {
    if (!this.bodyCanvas || !this.bodyCtx) return;
    this.bodyCtx.strokeStyle = config.resourceDriverColor;
    this.bodyCtx.lineWidth = 1;

    let startY = 0;
    if (resources.length === 0) {
      startY += config.resourceBodyRowHeight;
      this.bodyCtx.beginPath();
      this.bodyCtx.moveTo(config.resourceColumnWidth, startY);
      this.bodyCtx.lineTo(this.bodyCanvas.width, startY);
      this.bodyCtx.stroke();
      return;
    }
    for (let i = 0; i < resources.length; i++) {
      startY += config.resourceBodyRowHeight;
      this.bodyCtx.beginPath();
      this.bodyCtx.moveTo(0, startY);
      this.bodyCtx.lineTo(this.bodyCanvas.width, startY);
      this.bodyCtx.stroke();
    }
  }

  /**
   * @description 绘制背景
   * @param {UICoordinateController} uiCoordinate
   * @param {IScheduleResource[]} resources
   * @returns {*}  {void}
   * @memberof BgLayerController
   */
  draw(
    uiCoordinate: UICoordinateController,
    resources: IScheduleResource[],
  ): void {
    this.uiCoordinate = uiCoordinate;
    if (
      !this.uiCoordinate ||
      !this.headerCanvas ||
      !this.headerCtx ||
      !this.bodyCanvas ||
      !this.bodyCtx
    )
      return;
    this.copyResources = resources;
    const config = this.configController.getConfig();
    this.initCanvasSize(config);
    this.clearCanvas();
    this.drawHeader(config);
    this.drawResourceAndScaleBg(config);
    this.drawGridLines(config);
    this.drawActiveCell(config);
    this.drawScaleDrivers(config, resources);
    this.drawResourceDrivers(config, resources);
  }

  /**
   * @description 销毁
   * @memberof BgLayerController
   */
  destory(): void {
    this.bodyCanvas?.removeEventListener('click', this.handleCanvasClick);
    // 添加文档点击事件监听，用于清除激活状态
    document.removeEventListener('click', this.handleDocumentClick);
  }
}
